/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2022年11月28日
 * V4.0
 */
package com.jphenix.driver.udp;

import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

import com.jphenix.standard.docs.ClassInfo;

/**
 * UDP 消息信息容器
 * 
 * 注意：该容器与 MsgStateVO容器的区别：
 * 当前容器主要用于解析报文，解析报文后，针对报文内容，才会构建MsgStateVO
 * 
 * 注意：
 * 消息段分隔符为 0x0B 以下用 | 表示
 * 消息总长度不能超过548字节，如果消息内容过长，则会忽略消息内容，只发送消息代码
 * 
 * 首次请求报文： 系统代码 | token
 * 普通请求报文： 系统代码 | token | 目标服务名 | 传入参数
 * 
 * 服务端返回报文：
 *   重定向报文：  R|sysCode|token|端口|地址（可为空）
 *   普通消息信息：M|sysCode|token|消息代码|消息文字
 *   收到请求确认：Z|sysCode|token
 *   错误信息报文：E|错误代码|错误信息|sysCode|token （因为有些错误的请求中，并不包含sysCode 或 token）
 * 
 * 通信逻辑：
 * 1. 客户端通过HTTP协议向服务端获取token值
 * 2. 服务端在返回token值之前，需要调用 server中的regist方法，注册token值 
 *    （需要在服务端先注册后，客户端才能用UDP往服务端推送消息，否则容易被恶意攻击，导致服务端保存大量的无用数据）
 * 3. 客户端收到token值后，需要首次调用udp接口
 * 4. 首次调用后，可能会返回重定向信息。在收到重定向信息后，需要执行首次访问重定向后的地址，完成首次通信。
 * 5. 只有完成首次调用UDP接口后，服务端才能主动往客户端推送数据
 * 6. 在客户端好久没收到服务端udp数据时，可以重新获取token值，再执行首次访问udp服务端。
 * 
 * 用途：
 * 1. 主要用于主动通知客户端简短的消息。避免客户端定时轮训调用http访问接口。
 * 2. 如果需要服务端传递大块数据到客户端，可以先通过udp通知客户端，然后客户端调用http接口获取大量数据
 * 3. 不要用udp传递大块数据，从设计角度，也不是做这个用的。
 * 
 * @author mbg
 * 2022年11月28日
 */
@ClassInfo({"2022-11-29 14:45","UDP 消息信息容器"})
public class MsgVO {

	//#region 常量声明区
	// 报文长度：标准MTU值为576字节,最好将UDP的数据长度控件在548字节(576-8-20)以内.（8为UDP头。20为IP头）
	// 最大报文长度
	public final static int MAX_LENGTH = 548;
	
	private final static byte MSG_SEPARATOR = 0x0B; // 消息段分隔符 0x0B '|'
	//#endregion
	
	//#region 变量声明区
	// 系统代码
	public String sysCode     = null;
	
	// token值
	public String token       = null;
	
	// 服务代码
	public String serviceCode = null;
	
	// 提交数据
	public String parameter   = null;
	
	// 处理接收到的消息是否存在错误
	public boolean error      = false;
	
	private Charset         charset = StandardCharsets.UTF_8; //编码转换器
	//#endregion
	
	//#region MsgVO() 构造函数
	public MsgVO() {
		super();
	}
	//#endregion
	
	//#region MsgVO(buffer) 构造函数
	/**
	 * 构造函数
	 * @author mbg
	 */
	public MsgVO(ByteBuffer buffer) {
		super();
		parse(buffer); // 解析报文
	}
	//#endregion
	
	//#region parse 解析报文
	/**
	 * 解析报文
	 * @param buffer 报文缓存
	 * 2022年11月28日
	 * @author mbg
	 */
	public void parse(ByteBuffer buffer) {
		sysCode     = null;
		token       = null;
		serviceCode = null;
		parameter   = null;
		error       = false;
		if(buffer==null) {
			return;
		}
		buffer.flip();
		// 数据长度
		int length = buffer.limit();
		// 缓存段
		ByteBuffer subBb = ByteBuffer.allocate(length);
		// 数据位置
		int level = 0;
		byte ele;
		for(int i=0;i<length;i++) {
			ele = buffer.get(i);
			if(ele==MSG_SEPARATOR) {
				subBb.flip();
				if(level==0) {
					// 系统代码
					sysCode = charset.decode(subBb).toString();
				}else if(level==1) {
					// token
					token = charset.decode(subBb).toString();
				}else if(level==2) {
					// 服务名
					serviceCode = charset.decode(subBb).toString();
				}
				subBb.clear();
				level ++;
			}else {
				subBb.put(ele);
			}
		}
		if(level==3 && subBb.position()>0) {
			subBb.flip();
			parameter = charset.decode(subBb).toString();
		}
		if(sysCode==null || sysCode.length()<1 || token==null || token.length()<1) {
			error = true;
		}
	}
	//#endregion
	
	//#region toString 覆盖函数
	/**
	 * 覆盖函数
	 */
	public String toString() {
		return "sysCode:["+sysCode+"] token:["+token+"] serviceCode:["+
	      serviceCode+"] parameter:["+parameter+"]";
	}
	//#endregion
	
	//#region respError(sysCode,token,errorCode,errorMsg) 返回错误信息
	/**
	 * 返回错误信息
	 * @param errorCode  错误代码
	 * @param errorMsg   错误信息
	 * @param sysCode    系统代码
	 * @param token      token
	 * @return           报文
	 * 2022年11月28日
	 * @author mbg
	 */
	public static ByteBuffer respError(String errorCode,String errorMsg,String sysCode,String token) {
		if(errorCode==null || errorCode.length()<1) {
			return null;
		}
		// 构建返回值
		ByteBuffer res = ByteBuffer.allocate(MAX_LENGTH);
		res.put((byte)'E');
		res.put(MSG_SEPARATOR);
		
		//#region 添加错误代码
		byte[] errorCodeBytes = errorCode.getBytes();
		if(errorCodeBytes.length+2>MAX_LENGTH) {
			return null;
		}
		res.put(errorCodeBytes);
		//#endregion
		
		//#region 添加错误信息
		try {
			if(errorMsg!=null && errorMsg.length()>0) {
				byte[] errorMsgBytes = errorMsg.getBytes("UTF-8");
				if(res.position()+1+errorMsgBytes.length>MAX_LENGTH) {
					return res;
				}
				res.put(MSG_SEPARATOR);
				res.put(errorMsgBytes);
			}
		}catch(Exception e) {
			e.printStackTrace();
			return null;
		}
		//#endregion
		
		//#region 添加系统代码 sysCode
		if(sysCode!=null && sysCode.length()>0) {
			byte[] sysCodeBytes = sysCode.getBytes();
			if(res.position()+1+sysCodeBytes.length>MAX_LENGTH) {
				return res;
			}
			res.put(MSG_SEPARATOR);
			res.put(sysCodeBytes);
		}
		//#endregion
		
		//#region 添加 token
		if(token!=null && sysCode.length()>0) {
			byte[] tokenBytes = token.getBytes();
			if(res.position()+1+tokenBytes.length>MAX_LENGTH) {
				return res;
			}
			res.put(MSG_SEPARATOR);
			res.put(tokenBytes);
		}
		//#endregion
		
		return res;
	}
	//#endregion
	
	//#region response(sysCode,token,msgCode)  返回消息报文
	/**
	 * 返回消息报文
	 * @param sysCode   系统代码
	 * @param token     token
	 * @param msgCode   消息代码
	 * @param msg       消息内容
	 * @return          报文
	 * 2022年11月28日
	 * @author mbg
	 */
	public static ByteBuffer response(String sysCode,String token,String msgCode) {
		return response(sysCode,token,msgCode,null);
	}
	//#endregion
	
	//#region response(sysCode,token,msgCode,msg)  返回消息报文
	/**
	 * 返回消息报文
	 * @param sysCode   系统代码
	 * @param token     token
	 * @param msgCode   消息代码
	 * @param msg       消息内容
	 * @return          报文
	 * 2022年11月28日
	 * @author mbg
	 */
	public static ByteBuffer response(String sysCode,String token,String msgCode,String msg) {
		if(msgCode==null || msgCode.length()<1) {
			return null;
		}
		// 拼装基本返回信息
		ByteBuffer res = baseResponse('M',sysCode,token);
		if(res==null) {
			return null;
		}
		//#region 处理消息代码
		byte[] msgCodeBytes = msgCode.getBytes();
		if(res.position()+msgCodeBytes.length+1>MAX_LENGTH) {
			return null;
		}
		res.put(msgCodeBytes);
		//#endregion
		
		//#region 处理消息内容
		if(msg!=null && msg.length()>0) {
			try {
				byte[] msgBytes = msg.getBytes("UTF-8");
				if(res.position()+msgBytes.length+1>MAX_LENGTH) {
					return res;
				}
				res.put(MSG_SEPARATOR);
				res.put(msgBytes);
			}catch(Exception e) {
				e.printStackTrace();
			}
		}
		//#endregion
		
		return res;
	}
	//#endregion
	
	//#region response(sysCode,token,port) 返回重定向信息
	/**
	 * 返回重定向信息
	 * @param sysCode  系统代码
	 * @param token    token
	 * @param port     重定向端口
	 * @return         报文
	 * 2022年11月28日
	 * @author mbg
	 */
	public static ByteBuffer response(String sysCode,String token,int port) {
		return response(sysCode,token,port,null);
	}
	//#endregion
	
	//#region response(sysCode,token,port,address) 返回重定向信息
	/**
	 * 返回重定向信息
	 * @param sysCode  系统代码
	 * @param token    token
	 * @param port     重定向端口
	 * @param address  重定向地址 可为空
	 * @return         报文
	 * 2022年11月28日
	 * @author mbg
	 */
	public static ByteBuffer response(String sysCode,String token,int port,String address) {
		if(port<1) {
			return null;
		}
		// 拼装基本返回信息
		ByteBuffer res = baseResponse('R',sysCode,token);
		if(res==null) {
			return null;
		}
		res.putInt(port);
		if(address!=null && address.length()>0) {
			res.put(MSG_SEPARATOR);
			res.put(address.getBytes());
		}
		return res;
	}
	//#endregion
	
	//#region response(sysCode,token)  无信息返回
	/**
	 * 无信息返回
	 * @param sysCode  系统代码
	 * @param token    token
	 * @return 报文
	 * 2022年11月28日
	 * @author mbg
	 */
	public static ByteBuffer response(String sysCode,String token) {
		// 拼装基本返回信息
		return baseResponse('Z',sysCode,token);
	}
	//#endregion
	
	//#region baseResponse 拼装基本返回报文
	/**
	 * 拼装基本返回报文
	 * @param sysCode 系统代码
	 * @param token   token
	 * @return        基本返回报文
	 * 2022年11月28日
	 * @author mbg
	 */
	private static ByteBuffer baseResponse(char typeCode,String sysCode,String token) {
		if(sysCode==null || sysCode.length()<1 || token==null || token.length()<1) {
			return null;
		}
		byte[] sysCodeBytes = sysCode.getBytes();
		byte[] tokenBytes   = token.getBytes();
		// 构建返回值
		ByteBuffer res = ByteBuffer.allocate(MAX_LENGTH);
		res.put((byte)typeCode);
		res.put(MSG_SEPARATOR);
		res.put(sysCodeBytes);
		res.put(MSG_SEPARATOR);
		res.put(tokenBytes);
		res.put(MSG_SEPARATOR);
		return res;
	}
	//#endregion
}



